Alarm Live Activity

AlarmLiveActivity 允许脚本在 iOS 26+ 上,为通过 AlarmManager 创建的闹钟提供自定义锁屏和灵动岛 UI。

当内置闹钟 Live Activity UI 不能满足需求时,可以用它让闹钟展示更贴近脚本自身产品风格,同时继续使用 AlarmKit 的调度、暂停、继续、倒计时和停止能力。


文件

自定义闹钟 Live Activity UI 必须注册在独立文件中:

alarm_live_activity.tsx

主脚本仍然在 index.tsx 中创建闹钟。如果当前脚本没有提供 alarm_live_activity.tsx,或者指定的 UI 名称没有注册,Scripting 会回退到内置闹钟 UI。


注册 UI

AlarmLiveActivity 从 scripting 包导入:

import {
  AlarmLiveActivity,
  Text,
  LiveActivityUI,
  LiveActivityUIExpandedBottom,
  LiveActivityUIExpandedCenter,
  LiveActivityUIExpandedLeading,
  LiveActivityUIExpandedTrailing,
} from "scripting"

AlarmLiveActivity.register("FocusTimerActivity", state => {
  return (
    <LiveActivityUI
      content={<Text>{state.title}</Text>}
      compactLeading={<Text>{state.mode}</Text>}
      compactTrailing={<Text>{state.title}</Text>}
      minimal={<Text>{state.mode}</Text>}>
      <LiveActivityUIExpandedLeading>
        <Text>{state.title}</Text>
      </LiveActivityUIExpandedLeading>
      <LiveActivityUIExpandedTrailing>
        <Text>{state.mode}</Text>
      </LiveActivityUIExpandedTrailing>
      <LiveActivityUIExpandedCenter>
        <Text>{state.presentation.alert.title}</Text>
      </LiveActivityUIExpandedCenter>
      <LiveActivityUIExpandedBottom>
        <Text>{state.alarmID}</Text>
      </LiveActivityUIExpandedBottom>
    </LiveActivityUI>
  )
})

返回的根节点必须是 LiveActivityUI。content、compactLeading、compactTrailing 和 minimal 区域是必填项。


静态渲染与组件限制

Alarm Live Activity 与普通 Live Activity 一样,将 builder 返回的 UI 构建为静态快照,显示期间不会持续运行 JavaScript 来重新生成视图。

  • 不支持 GeometryReader、ScrollViewReader、NamespaceReader 和 EnvironmentValuesReader 等需要在显示期间将布局或环境信息回传给 JavaScript、再生成子视图的组件。使用这些组件可能导致对应内容为空。
  • 可以使用 HStack、VStack、Text、Image 等组件,也可以使用自定义函数组件和根据 AlarmLiveActivityState 进行的条件渲染;它们的内容在构建快照时确定。
  • 不要依赖视图内的 JavaScript 定时器、事件回调或 useState 持续驱动界面刷新。
  • 闹钟状态变化仍然可以更新内容。 AlarmKit 提供当前闹钟状态,builder 根据新状态构建新的 UI 快照,例如切换倒计时、暂停和响铃布局;实际显示的刷新时机由系统管理。AlarmLiveActivity 只负责注册 UI,不提供普通 LiveActivity 实例的 update(state) 方法。

倒计时可以使用由系统更新的 TimerIntervalLabel。暂停、继续、停止等按钮仍可通过 state.actions.*.intent 执行原生闹钟操作,无需保持视图的 JavaScript 持续运行。


绑定到闹钟

AlarmManager 是全局 API,不需要导入。

const attributes = AlarmManager.Attributes.create({
  alert: AlarmManager.AlertPresentation.create({
    title: "Focus complete",
    stopButton: AlarmManager.Button.create({
      title: "Done",
      systemImageName: "checkmark"
    })
  }),
  countdown: AlarmManager.CountdownPresentation.create(
    "Deep focus",
    AlarmManager.Button.create({
      title: "Pause",
      systemImageName: "pause.fill"
    })
  ),
  paused: AlarmManager.PausedPresentation.create(
    "Paused",
    AlarmManager.Button.create({
      title: "Resume",
      systemImageName: "play.fill"
    })
  ),
  tintColor: "#ff9f0a",
  metadata: {
    task: "Writing"
  },
  liveActivity: {
    name: "FocusTimerActivity"
  }
})

liveActivity.name 必须和 AlarmLiveActivity.register 传入的名称一致。


渲染状态

builder 会收到一个 AlarmLiveActivityState:

type AlarmLiveActivityState = {
  alarmID: string
  mode: "scheduled" | "countdown" | "paused" | "alerting"
  title: string
  tintColor?: Color | null
  metadata: Record<string, string>
  presentation: AlarmLiveActivityPresentation
  actions: {
    pause?: AlarmLiveActivityAction | null
    resume?: AlarmLiveActivityAction | null
    stop: AlarmLiveActivityAction
    secondary?: AlarmLiveActivityAction | null
  }
  countdown?: {
    fireDate: Date
    totalCountdownDuration: number
  } | null
  paused?: {
    totalCountdownDuration: number
    prePauseCountdownDuration: number
  } | null
}

可以使用 mode 切换布局,使用 metadata 展示业务数据,也可以复用 AlarmManager.Attributes 中配置的标题和按钮文案。


内置动作

actions 字段包含由 Scripting 为当前闹钟创建的 AppIntent 控件。

{state.actions.pause && (
  <Button intent={state.actions.pause.intent}>
    <Label title={state.actions.pause.title} systemImage={state.actions.pause.systemImageName ?? "pause.fill"} />
  </Button>
)}

<Button intent={state.actions.stop.intent} role="destructive">
  <Label title={state.actions.stop.title} systemImage={state.actions.stop.systemImageName ?? "xmark"} />
</Button>

这些 action 会调用当前闹钟对应的原生 AlarmManager 操作:

  • pause
  • resume
  • stop
  • 当 alert 的 secondary button 使用 secondaryButtonBehavior: "countdown" 时,提供 secondary

如果需要额外的脚本业务逻辑,可以另外注册自己的 AppIntent 并用于普通 Live Activity 按钮。控制闹钟自身状态时,推荐优先使用 state.actions.*.intent。


注意事项

  • AlarmManager 的 alert stopIntent 和 secondaryIntent 仍然属于系统 alert 展示。
  • 自定义 Live Activity UI 中的暂停、继续、停止和倒计时按钮应该使用 state.actions.*.intent。
  • 灵动岛 compact 区域要尽量小,优先使用 SF Symbols、短文案和系统字体。
  • iOS 26 上,锁屏和展开灵动岛中的可见控件建议使用 buttonStyle="glass" 或 buttonStyle="glassProminent"。

灵动岛外观

自定义闹钟 builder 支持 Live Activity 文档中的 LiveActivityUI.keylineTint/dynamicIslandContentMargins。省略 tint 继承闹钟颜色,显式 null 传原生 nil,显式 Color 覆盖闹钟色。分模式边距仅作用于灵动岛,不改变内置闹钟 UI 和锁屏外观。